package org.lds.hotkey.console.business.hotkey.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.lds.hotkey.common.autoconfigure.etcd.EtcdService;
import org.lds.hotkey.common.constant.KeyConstant;
import org.lds.hotkey.common.model.HotKeyModel;
import org.lds.hotkey.common.model.dto.ResultDTO;
import org.lds.hotkey.common.model.pojo.OrderBy;
import org.lds.hotkey.common.model.pojo.PageResult;
import org.lds.hotkey.common.util.AssertUtil;
import org.lds.hotkey.common.util.JsonUtil;
import org.lds.hotkey.console.business.app.service.AppService;
import org.lds.hotkey.console.business.hotkey.converter.*;
import org.lds.hotkey.console.business.hotkey.entity.HotKey;
import org.lds.hotkey.console.business.hotkey.mapper.HotKeyMapper;
import org.lds.hotkey.console.business.hotkey.model.request.*;
import org.lds.hotkey.console.business.hotkey.model.response.*;
import org.lds.hotkey.console.business.hotkey.service.HotKeyBaseService;
import org.lds.hotkey.console.common.enums.ConsoleErrorEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 *  基础服务实现类
 *
 * @author lidongsheng
 * @since 2024-06-18
 */
@Slf4j
@Service
public class HotKeyBaseServiceImpl implements HotKeyBaseService {

    @Autowired
    private HotKeyMapper hotKeyMapper;

    @Autowired
    private AppService appService;

    @Autowired
    private EtcdService etcdService;

    @Autowired
    private HotKeyBaseCreateRequestToEntityConverter hotKeyBaseCreateRequestToEntityConverter;

    @Autowired
    private HotKeyBaseUpdateRequestToEntityConverter hotKeyBaseUpdateRequestToEntityConverter;

    @Autowired
    private HotKeyBaseListRequestToEntityConverter hotKeyBaseListRequestToEntityConverter;

    @Autowired
    private HotKeyToBaseResponseConverter hotKeyToBaseResponseConverter;

    @Autowired
    private HotKeyBaseListPageRequestToEntityConverter hotKeyBaseListPageRequestToEntityConverter;

    @Autowired
    private HotKeyBaseGetByKeyRequestToEntityConverter hotKeyBaseGetByKeyRequestToEntityConverter;

    @Autowired
    private HotKeyBaseDeleteRequestToEntityConverter hotKeyBaseDeleteRequestToEntityConverter;

    @Override
    @Transactional
    public ResultDTO<HotKeyBaseResponse> createHotKey(HotKeyBaseCreateRequest request) {
        if(needCheckApp(request.getAppId())) {
            appService.existApp(request.getAppId());
        }

        try {
            HotKey hotKey = hotKeyBaseCreateRequestToEntityConverter.convert(request);
            hotKeyMapper.insert(hotKey);

            // 新增成功则同步到ETCD
            boolean success = etcdService.put(KeyConstant.HOT_KEY_DIRECTORY + hotKey.getAppId() + "/" + hotKey.getId(),
                    JsonUtil.toJson(HotKeyModel.builder()
                            .id(hotKey.getId())
                            .key(hotKey.getKey())
                            .duration(hotKey.getDuration())
                            .build()));
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            HotKeyBaseResponse response = hotKeyToBaseResponseConverter.convert( hotKey);
            return ResultDTO.success(response);
        }

        catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    private boolean needCheckApp(Long appId) {
        return appId != null && appId != 0L;
    }

    @Transactional
    @Override
    public ResultDTO<HotKeyBaseBatchCreateResponse> batchCreateHotKey(HotKeyBaseBatchCreateRequest request) {
        HotKeyBaseBatchCreateResponse response = HotKeyBaseBatchCreateResponse.builder()
            .responses(request.getRequests().stream().map(this::createHotKey).collect(Collectors.toList()))
            .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<HotKeyBaseDeleteResponse> deleteHotKey(HotKeyBaseDeleteRequest request) {
        HotKeyBaseDeleteResponse response = HotKeyBaseDeleteResponse.builder().request(request).build();

        HotKey hotKey = hotKeyBaseDeleteRequestToEntityConverter.convert(request);
        List<HotKey> hotKeys = hotKeyMapper.selectList(new QueryWrapper<>(hotKey));
        if (CollectionUtils.isEmpty(hotKeys)) return ResultDTO.success(response);

        hotKeyMapper.delete(new QueryWrapper<>(hotKey));

        // 删除成功同步ETCD
        for (HotKey currHotKey : hotKeys) {
            boolean success = etcdService.delete(KeyConstant.HOT_KEY_DIRECTORY + currHotKey.getAppId() + "/" + currHotKey.getId());
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);
        }

        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<HotKeyBaseBatchDeleteResponse> batchDeleteHotKey(HotKeyBaseBatchDeleteRequest request) {
        HotKeyBaseBatchDeleteResponse response = HotKeyBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().stream().map(this::deleteHotKey).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Transactional
    @Override
    public ResultDTO<HotKeyBaseResponse> updateHotKey(HotKeyBaseUpdateRequest request) {
        HotKey hotKey = hotKeyBaseUpdateRequestToEntityConverter.convert(request);
        HotKeyBaseResponse response = hotKeyToBaseResponseConverter.convert(hotKey);

        HotKey currHotKey = hotKeyMapper.selectById(request.getId());
        if (currHotKey == null) return ResultDTO.success(response);

        try {
            hotKeyMapper.updateById(hotKey);

            boolean success = updateETCD(request, currHotKey);
            AssertUtil.assertTrue(success, ConsoleErrorEnum.ETCD_SYNC_FAIL);

            return ResultDTO.success(response);
        }

        catch (DuplicateKeyException e) {
            return ResultDTO.fail("DATA_REPEAT", "数据重复");
        }
    }

    private boolean updateETCD(HotKeyBaseUpdateRequest request, HotKey currHotKey) {
        if (!currHotKey.getEnable() && !request.getEnable()) return true;
        if (currHotKey.getEnable() && BooleanUtils.isFalse(request.getEnable())) {
            return etcdService.delete(KeyConstant.HOT_KEY_DIRECTORY + currHotKey.getAppId() + "/" + currHotKey.getId());
        }
        return etcdService.put(KeyConstant.HOT_KEY_DIRECTORY + currHotKey.getAppId() + "/" + currHotKey.getId(),
                JsonUtil.toJson(HotKeyModel.builder()
                        .id(request.getId())
                        .key(request.getKey())
                        .duration(request.getDuration())
                        .build()));
    }

    @Transactional
    @Override
    public ResultDTO<HotKeyBaseBatchUpdateResponse> batchUpdateHotKey(HotKeyBaseBatchUpdateRequest request) {
        HotKeyBaseBatchUpdateResponse response = HotKeyBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().stream().map(this::updateHotKey).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseResponse> getHotKeyById(HotKeyBaseGetByIdRequest request) {
        HotKey hotKey =  hotKeyMapper.selectById(request.getId());
        if (hotKey == null) {
            return ResultDTO.success();
        }

        HotKeyBaseResponse response = hotKeyToBaseResponseConverter.convert(hotKey);
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseListResponse> getHotKeyByIds(HotKeyBaseGetByIdsRequest request) {
        List<HotKey> list = request.getIds().stream().map(id -> hotKeyMapper.selectById(id)).collect(Collectors.toList());
        return ResultDTO.success(HotKeyBaseListResponse.builder()
                .hotKeys(list.stream().map(hotKey -> hotKeyToBaseResponseConverter.convert(hotKey)).collect(Collectors.toList()))
                .build());
    }

    @Override
    public HotKey getHotKeyByKey(HotKeyBaseGetByKeyRequest request) {
        return  hotKeyMapper.selectOne(new QueryWrapper<>(hotKeyBaseGetByKeyRequestToEntityConverter.convert(request)));
    }

    @Override
    public ResultDTO<HotKeyBaseListResponse> listHotKey(HotKeyBaseListRequest request) {
        QueryWrapper<HotKey> wrapper = new QueryWrapper<>(hotKeyBaseListRequestToEntityConverter.convert(request));
        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            wrapper.orderByDesc(request.getOrderByKey());
        }

        List<HotKey> hotKeys =  hotKeyMapper.selectList(wrapper);

        HotKeyBaseListResponse response = HotKeyBaseListResponse.builder()
                    .hotKeys(hotKeys.stream().map(hotKeyToBaseResponseConverter::convert).collect(Collectors.toList()))
                    .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseListResponse> listAllHotKey(OrderBy orderBy) {
        HotKeyBaseListRequest hotKeyRequest = HotKeyBaseListRequest.builder().build();
        hotKeyRequest.setOrderByKey(orderBy.getOrderByKey());
        return listHotKey(hotKeyRequest);
    }

    @Override
    public ResultDTO<PageResult<HotKeyBaseResponse>> listHotKeyPage(HotKeyBaseListPageRequest request) {
        Page<HotKey> page = new Page<>(request.getPageNum(), request.getPageSize(), true);

        QueryWrapper<HotKey> wrapper = new QueryWrapper<>(hotKeyBaseListPageRequestToEntityConverter.convert(request));
        if (StringUtils.isNotEmpty(request.getOrderByKey())) {
            if (request.getOrderByDesc() != null && request.getOrderByDesc().booleanValue() == false) {
                wrapper.orderByAsc(request.getOrderByKey());
            } else {
                wrapper.orderByDesc(request.getOrderByKey());
            }
        }

        IPage<HotKey> pageResult =  hotKeyMapper.selectPage(page, wrapper);

        PageResult<HotKeyBaseResponse> response = new PageResult(
        pageResult.getTotal(), pageResult.getPages(), pageResult.getCurrent(), pageResult.getSize(),
        pageResult.getRecords().stream().map(hotKeyToBaseResponseConverter::convert).collect(Collectors.toList()));
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseBatchCreateResponse> batchCreateHotKeyAsync(HotKeyBaseBatchCreateRequest request) {
        HotKeyBaseBatchCreateResponse response = HotKeyBaseBatchCreateResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::createHotKey).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseBatchDeleteResponse> batchDeleteHotKeyAsync(HotKeyBaseBatchDeleteRequest request) {
        HotKeyBaseBatchDeleteResponse response = HotKeyBaseBatchDeleteResponse.builder()
                .responses(request.getRequests().parallelStream().map(this::deleteHotKey).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

    @Override
    public ResultDTO<HotKeyBaseBatchUpdateResponse> batchUpdateHotKeyAsync(HotKeyBaseBatchUpdateRequest request) {
        HotKeyBaseBatchUpdateResponse response = HotKeyBaseBatchUpdateResponse.builder()
                .requests(request.getRequests().parallelStream().map(this::updateHotKey).collect(Collectors.toList()))
                .build();
        return ResultDTO.success(response);
    }

}
